import copy
import pickle
import math


# 'CNRP' means 'Current Nodes Reputations'
# 即当前所有节点的信誉值 , reputation 为下一步工作的核心内容
# 故类似于 transaction 的简称 tx, 下文均称 reputation 为 rp

# 该类主要包含计算方法及 local_rp 的存储管理
# global_rp 计算及应用主要在 block_chain 中调用


# ---- 计算配置项 ---------------------

# 窗口大小
CNRP_WINDOW_LENGTH = 1000

# ---- 计算相关值 ---------------------

# b_punish: 惩罚系数 4
CNRP_b_punish = 2
# R_init: 初始信誉值
CNRP_R_init = 0.5

# 初始 E 值
CNRP_E_init = 0.5

# 打分补足阈值 当打分个数小于该阈值时，自动补齐该阈值个数的虚拟节点的打分记录
CNRP_threshold_rp_num = 3
# 虚拟节点的默认 S_ab 取值 0.5
CNRP_S_virtual_node = 0.5



CNRP_WEIGHT_DICT = {
    'basic': 0.5,                    # 基本初始 rp
    'genesis_block': 0.001,          # 对 创建创世块 执行者的奖励值 执行者：指该块的发布者，执行该命令的主体。与之相对的是接受者
    'upload': 0.001,                 # 对 upload 执行者的奖励值
    'request': 0,                    # 对 request 执行者的奖励值
    'request_rcv': 0.001,            # 对 request 接受者的奖励值 接受者(rcv)：指命令的被动承受者 例如该条为数据上传者
    'share': 0.002,                  # 对 share 执行者的奖励值
    'share_rcv': 0.001,              # 对 share 接受者的奖励值
    'evaluate': 0.001,               # 对 evaluate 执行者的奖励值
    'evaluate_rcv': 0.001,           # 对 evaluate 接受者的奖励值
    'data_rp': 0.01,                 # data_rp 的满分评价在 global_rp 上的映射 即data评价为满分（百分制）则节点rp增加0.01

    'local_rp_request_o2m': 0.001,   # 他人请求我的数据 我对他人的好感度增加0.001
    'local_rp_share_o2m': 0.002,     # 他人同意授权给我数据 我对他人的好感度增加0.002
    'local_rp_evaluate_m2o': 0.002,  # 我给他人数据评价 满分数据对他人好感度增加0.002
    'local_rp_evaluate_o2m': 0.002,  # 他人对我的数据评价 满分数据对他人好感度增加0.002
}


class GlobalCNRPCounter:

    # def __init__(self):
    #     pass

    # 计算第一部分总结果 n为统计数目
    @staticmethod
    def compute_B_n(n):
        B_n = 1 - math.exp(-n)
        return B_n

    # 计算 S_ab 其中pos_neg为list
    @staticmethod
    def compute_S_ab(pos_neg):
        S_ab = round((pos_neg[0] - CNRP_b_punish * pos_neg[1]) / (pos_neg[0] + CNRP_b_punish * pos_neg[1]), 4)
        return S_ab


# 本地local_rp表计算和管理
# ！注意：本地 local_rp 指与 global_rp 相对应的rp字典，起加成作用，计算时与global_rp相应字段加和
class LocalCNRP(object):

    CNRP_DICT_FILE_NAME = 'local_data/local_cnrp.dat'

    def __init__(self, cnrp_file_name=CNRP_DICT_FILE_NAME):
        self.file_name = cnrp_file_name
        try:
            with open(cnrp_file_name, 'rb') as f:
                self.dict = pickle.load(f)
        except IOError:
            self.dict = {'height': 0, 'cnrp': {}}

    def has_local_cnrp(self):
        if self.dict == {'height': 0, 'cnrp': {}}:
            return False
        else:
            return True

    def delete_local_cnrp(self):
        self.dict = {'height': 0, 'cnrp': {}}
        self._save()

    def get_rp(self, address):
        return self.dict['cnrp'].get(address, 0)

    def has_key(self, address):
        return address in self.dict['cnrp']

    # 增量更新
    def refresh(self, address, addition):
        _value = self.dict['cnrp'].get(address, 0)
        self.dict['cnrp'][address] = _value + addition
        self._save()

    def refresh_height(self, height):
        self.dict['height'] = height

    # 更新原有数据
    def update(self, address, rp, height):
        self.dict['cnrp'][address] = rp
        self.dict['height'] = height
        self._save()

    def get_height(self):
        return self.dict['height']

    def get_complete_dict(self):
        return self.dict

    # 可选参数 降序排序
    def get_cnrp_dict(self, sort=False):
        _cnrp_dict = self.dict['cnrp']
        if sort:
            return sorted(_cnrp_dict.items(), key=lambda i: i[1], reverse=True)
        return _cnrp_dict

    def _save(self):
        with open(self.file_name, 'wb') as f:
            pickle.dump(self.dict, f)
